import json

from carts.utils import merge_cart_cookie_to_redis
from users import contants
from django.shortcuts import render, redirect
from django.views import View
from django import http  #响应浏览器
from django.contrib.auth import login, authenticate, logout
#authenticate读取配置文件Back
#end路径，打开配置文件的包，通过包调用方法，实际上就是调用了auth_backends的包
import re
from django.conf import settings
from meiduo_mall.utils import meiduo_signature  #用户加密和解密
from users.models import User, Address
from meiduo_mall.utils.response_code import RETCODE
from django.contrib.auth.mixins import LoginRequiredMixin #django自带判断用户是否登陆
from celery_tasks.mail.tasks import send_user_email     #django 调用任务
from  django_redis import get_redis_connection

class Index(View):
    def get(self,request):
        return render(request,'index.html')
# #
class RegisterView(View):
    def get(self, request):
        return render(request, 'register.html')

    def post(self, request):
        # 1.接收
        user_name = request.POST.get('user_name')
        pwd = request.POST.get('pwd')
        cpwd = request.POST.get('cpwd')
        phone = request.POST.get('phone')
        allow = request.POST.get('allow')
        sms_code_request = request.POST.get('msg_code')
        #验证
        # 非空
        if not all([user_name, pwd, cpwd, phone, allow]):
            return http.HttpResponseBadRequest('参数不完整')
        # 2用户名
        if not re.match('^[a-zA-Z0-9_-]{5,20}$', user_name):
            return http.HttpResponseBadRequest('请输入5-20个字符的用户名')
        if User.objects.filter(username=user_name).count() > 0:
            return http.HttpResponseBadRequest('用户名已存在')
        # 3密码
        if not re.match('[0-9A-Za-z]{8,20}$', pwd):
            return http.HttpResponseBadRequest('请输入8-20位的密码')
        if pwd != cpwd:
            return http.HttpResponseBadRequest('两次密码不一致')
        # 2.4手机号
        if not re.match('^1[345789]\d{9}', phone):
            return http.HttpResponseBadRequest('您输入的手机号格式不正确')
        if User.objects.filter(mobile=phone).count() > 0:
            return http.HttpResponseBadRequest('手机号已存在')
         # 2.5短信验证码
        # 连接redis
        redis_cli = get_redis_connection('verify_code')
        # 读取短信验证码
        sms_code_redis = redis_cli.get('sms_' + phone)
        # 判断是否过期
        if not sms_code_redis:
            return http.HttpResponseBadRequest('短信验证已过期')
        # 强制立即过期
        redis_cli.delete('sms_' + phone)
        # 判断短信验证码是否正确
        if sms_code_redis.decode() != sms_code_request:
            return http.HttpResponseBadRequest('短信验证码错误')

        # 3.处理
        # 创建用户对象，保存到表中，先将密码加密，再保存到表中
        # user = User.objects.create(username=user_name, password=pwd, mobile=phone)
        user = User.objects.create_user(username=user_name, password=pwd, mobile=phone)

        # 状态保持
        # request.session['user_id']=user.id
        login(request, user)

        # 响应
        response =redirect('/')
        # 登陆成功能后合并购物车
        response = merge_cart_cookie_to_redis(request, response)

        response.set_cookie('username',user.username, max_age = 3600 * 24 * 15)

        return response
#这个逻辑表示注册成功后直接到首页，也就表示登陆成功

class UsernameCountView(View):
    def get(self, request, username):
        # 接收
        # 验证
        # 处理：查询判断
        count = User.objects.filter(username=username).count()
        # 响应：如果是ajax请求，则返回json数据
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'OK',
            'count': count
        })


class MobileCountView(View):
    def get(self, request, mobile):
        # 接收
        # 验证
        # 处理：查询统计
        count = User.objects.filter(mobile=mobile).count()

        # 响应
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'OK',
            'count': count
        })
#登陆
class LoginView(View):
    def get(self,request):
        return render(request,'login.html')
    def post(self,request):
        #接收信息
        username=request.POST.get('username')
        pwd=request.POST.get('pwd')
        remembered = request.POST.get('remembered')
        #接受重定向参数(返回到指定的首页)，当你返回后就可以在地址栏看到一个next, next表示在没有登陆前访问的页面，
        next_url =request.GET.get('next','/')
        #验证
        if not all([username,pwd,remembered]):
            return http.HttpResponseBadRequest('参数不全不完整')
            # 判断用户名是否是5-20个字符
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return http.HttpResponseForbidden('请输入正确的用户名或手机号')

        # 判断密码是否是8-20个数字
        if not re.match(r'^[0-9A-Za-z]{8,20}$', pwd):
            return http.HttpResponseForbidden("密码最少8位，最长20位字符")

        #处理
        user=authenticate(username=username,password=pwd) #调用auth_backedns里面的方法
        # 判断用户是否符合要求username 可以表示密码和用户的名字

        if user is None:
            return render(request,'login.html',{'login_error':'小姐姐你输入的用户或者密码错误'})
        else:

            login(request, user)
        # # 设置状态保持的周期
        # if remembered != 'on':
        #     # 没有记住用户：浏览器会话结束就过期,就是在你退出网页的时候
        #     request.session.set_expiry(0)
        # else:
        #     # 记住用户：None表示两周后过期
        #     request.session.set_expiry(None)
            response = redirect(next_url)   #这里的url是变量
            # 登陆成功能后合并购物车
            response = merge_cart_cookie_to_redis(request, response)
            response.set_cookie('username', user.username, max_age=3600 * 24 * 15)
            # 设置cookie 在前端标签引用变量在网页上显示登陆状态，保存会话状态
            return response

#退出前端设置超链接
class LogoutView(View):
    def get(self,request):
        logout(request)
        response = redirect('/')#退出到首页后地址栏有个next
        response.delete_cookie('username')
        return response
#渲染个人中心网页
class Info(LoginRequiredMixin,View):
    def get(self,request):
        # if request.user.is_authenticated:#user=authenticate(username=username,password=pwd),表示用户
        #     #判断用户是否是登陆的
        #     return render(request,'user_center_info.html')
        # else:
        #     return redirect('/login/')#重定向到登陆页面 ,用了上面的类方法，在设置里面指定判断退出后的页面首页路径

        context={
            'username':request.user.username,
            'mobile':request.user.mobile,
            'email':request.user.email,
            "email_active":request.user.email_active

        }
        return render(request,'user_center_info.html',context)

#邮件创建
class EmailView(LoginRequiredMixin,View):#第一个类就是判断用户是否登陆，没有
    #登陆就返回到登录页面
    def put(self,request):
        #接受
        # email1 = request.body.get('email')
        # #接受的数据是个beytie类型，需要转义成字符串的形式
        # email2 = email1.decode()
        # #字符串不方便储存和获取数据，这里转换成自电脑的形式
        # dict_email = json.loads(email2)
        # #获取数据
        # email = dict_email.get('email')
        #--------------
        dict = json.loads(request.body.decode())
        email = dict.get('email')
        #验证
        if not all([email]):
            return http.JsonResponse({
                'code':RETCODE.PARAMERR,
                'errmsg':'没有邮箱参数'
            })
        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return http.JsonResponse({
                'code':RETCODE.PARAMERR,
                'errmsg':'邮箱格式不符合要求'

            })
        #处理
        # 修改属性，user 实际就是创建数据库的一个User 的对象，
        user = request.user#  表示当前登陆的用户
        user.email=email     #赋值表示把当前修改的属性更改在数据库
        user.save()

        # 发邮件，先对user_id 加密，当点击链接激活时，回调路径回到当前页面
        token = meiduo_signature.dumps({'user_id': user.id}, contants.EMAIL_EXPIRES)
        verify_url = settings.EMAIL_VERIFY_URL + '?token=%s' % token #回调路径
        send_user_email.delay(email, verify_url)#delay调用异步

        #响应
        return http.JsonResponse({
            'code':RETCODE.OK,
            'errmsg': "ok"

        })

#邮件的创建，
# 创建邮件的激活，当邮件发送成功后，点击链接发送浏览器请求，get接收的方式，
#  这个时候需要显示邮件是否验证成功
class EmailVerifyView(View):
    def get(self, request):
    # 接收http://www.meiduo.site:8000/emails/verification/?token=eyJpYXQiOjE1NTY4NTgwMzQ
        token = request.GET.get('token')

        # 验证
        dict1 = meiduo_signature.loadds(token, contants.EMAIL_EXPIRES)
        if dict1 is None:
            return http.HttpResponseBadRequest('激活信息无效，请重新发邮件')
        user_id = dict1.get('user_id')

        # 处理
        try:
            user = User.objects.get(pk=user_id)#pk表示主键，在user中绑定的主键是id，pk=id ，
            # user_id表示在创建认证模型类中建立的外键
        except:
            return http.HttpResponseBadRequest('激活信息无效')
        else:
            user.email_active = True
            user.save()
            # print('小姐姐')

        # 响应
        return redirect('/info/')

#添加收货地址
class AddressView(LoginRequiredMixin,View):
    def get(self,request):
        # 查询当前用户的收货地址,显示当前用户的所有创建的地址  -----------------------------------
        user = request.user
        address_list = Address.objects.filter(user=user, is_delete=False)
        # 将python对象转换成字典，供js使用
        address_list2 = []
        for address in address_list:
            address_list2.append(address.to_dict())

        context = {
            'addresses': address_list2,  # [{},{},..]
            'user': user
        }
        return render(request,'user_center_site.html',context)# 收货地址的网页

#创建收货地址增加操作-----------------------------------------------------------------------------
class AddressCreateView(LoginRequiredMixin, View):
    def post(self, request):
        # 添加收货地址
        #点击表单的时候会发送一个ajax的请求，post将服务器的收货地址信息发送到js
        user = request.user   #当前登陆的用户
        # 接收
        '''
        request.GET===>查询参数
        request.POST===><form method='post'></form>
        request.body===>ajax请求中的json数据
        '''
        #---------接受数据，验证数据是否符合要求，处理数据存储在数据库，返回响应
        #ajax的post请求，接受的参数必须和前端传过来的数据相同
        dict1 = json.loads(request.body.decode())
        # title = dict1.get('title')
        receiver = dict1.get('receiver')
        province_id = dict1.get('province_id')
        city_id = dict1.get('city_id')
        district_id = dict1.get('district_id')
        #表示详细地址
        place = dict1.get('place')
        mobile = dict1.get('mobile')
        tel = dict1.get('tel')
        email = dict1.get('email')

        # 验证分为两部分 非空和合适
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '参数不完整'
            })
        # 格式验证电话号码的格式验证
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '手机号格式错误'
            })
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.JsonResponse({
                    'code': RETCODE.PARAMERR,
                    'errmsg': '固定电话格式错误'
                })
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.JsonResponse({
                    'code': RETCODE.PARAMERR,
                    'errmsg': '邮箱格式错误'
                })

        # 处理：添加数据将前端传的数据在数据库进行储存，创建对象***.objects.create()
        address = Address.objects.create(
            user=user, #表示当前登陆的用户在数据库有关联的user_id 显现用户的资料信息
            title=receiver,
            receiver=receiver,
            province_id=province_id,
            city_id=city_id,
            district_id=district_id,
            # 注意detail是数据库的字段名
            detail=place,
            mobile=mobile,
            phone=tel,
            email=email
        )
        # 判断当前用户是否有默认地址，如果没有则把当前建立的这个设置为地址
        if  user.default_address is None:
            user.default_address = address
            user.save()

        # 响应
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'ok',
            'address': address.to_dict()#用地址这个对象调用方法去响应前端
        })
#修改操作------------------------------------------------------------------------------
# 地址的修改，put的请求方式，接受，验证格式和非空，处理，修改数据
class AddressUpdateView(LoginRequiredMixin, View):
    def put(self, request, address_id):
        # 接收，以路径的形式传过来的
        dict1 = json.loads(request.body.decode())
        # title = dict1.get('title')
        receiver = dict1.get('receiver')
        province_id = dict1.get('province_id')
        city_id = dict1.get('city_id')
        district_id = dict1.get('district_id')
        place = dict1.get('place')
        mobile = dict1.get('mobile')
        tel = dict1.get('tel')
        email = dict1.get('email')

        # 验证
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '参数不完整'
            })
        # 格式验证
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '手机号格式错误'
            })
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return http.JsonResponse({
                    'code': RETCODE.PARAMERR,
                    'errmsg': '固定电话格式错误'
                })
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return http.JsonResponse({
                    'code': RETCODE.PARAMERR,
                    'errmsg': '邮箱格式错误'
                })

                # 处理：修改
        user = request.user
        try:
            address = Address.objects.get(pk=address_id, user=user, is_delete=False)
        except:
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '收货地址编号错误'
            })
    #修改属性，修改完成后保存在数据库，每次打开该网页的时候都会进行一个查询的操作，
    #当点击修改后服务器接受到需要修改用户的Id ,然后将需要修改用户的信息在数据库查询出来，并且将修改后的属性进行赋值，保存在数据库

        address.receiver = receiver
        address.province_id = province_id
        address.city_id = city_id
        address.district_id = district_id
        address.detail = place
        address.mobile = mobile
        address.phone = tel
        address.email = email
        address.save()  #将修改后的数据进行赋值，保存

        # 响应
        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'ok',
            'address': address.to_dict()  #赋值后的结果返回给前端
        })
#做删除操作---------------------------------------------------------------------
    def delete(self, request, address_id):
        # 查询对象，并修改is_delete属性为True
        try:
            address = Address.objects.get(pk=address_id, user=request.user, is_delete=False)
        except:
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '收货地址编号无效'
            })
        # 逻辑删除，重点是这句话，
        address.is_delete = True  #实际删除的操作，建立方法，不需要接受数据和验证，只需要查询当前用户，做逻辑删除后保存
        address.save()

        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'ok'
        })

#---------------修改默认地址，实际修改属性，做赋值就可以，做删除和小的改动时，不用返回数据只在数据库做修改储存
class AddressDefaultView(LoginRequiredMixin, View):
    def put(self, request, address_id):
        #只做处理。url 已经做了接受和验证，默认地址在user中关联
        user = request.user
        user.default_address_id = address_id
        user.save()

        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'ok'
        })

#--------------------------------------标题的修改
class AddressTitleView(LoginRequiredMixin, View):
    def put(self, request, address_id):
        dict1 = json.loads(request.body.decode())
        title = dict1.get('title')

        if not all([title]):
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '收货地址标题不能为空'
            })

        try:
            address = Address.objects.get(pk=address_id, user=request.user, is_delete=False)
        except:
            return http.JsonResponse({
                'code': RETCODE.PARAMERR,
                'errmsg': '收货地址编号无效'
            })
        address.title = title
        address.save()

        return http.JsonResponse({
            'code': RETCODE.OK,
            'errmsg': 'ok'
        })

#密码的修改------------------------------------------------------------------
class PasswordView(LoginRequiredMixin, View):
    def get(self, request):
        return render(request, 'user_center_pass.html')
    #模板渲染刷新页面，接受表单post请求。填写表单的内容保存后得到一个post请求
    def post(self, request):
        # 接收
        old_pwd = request.POST.get('old_pwd')
        new_pwd = request.POST.get('new_pwd')
        new_cpwd = request.POST.get('new_cpwd')

        # 验证
        # 非空
        if not all([old_pwd, new_pwd, new_cpwd]):
            return http.HttpResponseBadRequest('参数不完整')
        # 原密码是否正确
        user = request.user
        if not user.check_password(old_pwd):   #检查当前登陆的原始密码
            return http.HttpResponseBadRequest('你输入的原密码错误')
        # 格式
        if not re.match(r'^[0-9A-Za-z]{8,20}$', new_pwd):
            return http.HttpResponseBadRequest('新密码格式错误')
        # 新密码是否一致
        if new_pwd != new_cpwd:
            return http.HttpResponseBadRequest('新密码两次密码不一致')

        # 处理：修改密码
        user.set_password(new_pwd)
        user.save()

        # 响应
        #如果密码修改成功返回到登录页面进行再次登陆操作，这是我个人逻辑要求,，
        logout(request)
        response = redirect('/login/')
        response.delete_cookie('username')
        # return render(request, 'user_center_pass.html')
        return  response
